#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <ArduinoJson.h>
//#include <SPI.h>
#include "SPI.h"
//#include "SPIMaster.h"
#include <Arduino.h>
#include <Wire.h>
#include <TimeLib.h>
#include <time.h>
#include <WiFiUdp.h>
#include <math.h>
#include <SoftwareSerial.h>
//SoftwareSerial ESP_Serial(6, 7); //Tx,Rx


//---------------wifi连接/API用户名密码信息----------------
const char *ssid = "RNE-AL00";        //WiFi名
const char *password = "147258369";  //WiFi密码


const char* HOST = "http://api.seniverse.com";
const char* APIKEY = "SK6VinZcCGt_KXOQc";        //API KEY 知心天气私钥
const char* CITY = "Lanzhou";  //城市全拼
const char* LANGUAGE = "zh-Hans";//zh-Hans 简体中文 
//-------------------------------------------------------

//-----------------------天气参数-------------------------
typedef struct
{
    String date_m;
    String date_d;
    uint8_t code_day;
    int8_t high;
    int8_t low;
    uint8_t wind_direction_degree;
    uint8_t wind_scale;
    uint8_t humidity;
}weather_date; //天气信息的结构体

weather_date day0,day1,day2;//三天点名


//-----------------------定义常量-------------------------
int Click = 0;  //轻触开关引脚

const unsigned long HTTP_TIMEOUT = 5000;  //http访问请求

WiFiClient client;//客户端
HTTPClient http;  //http请求


const int slaveSelect = 5;
const int scanLimit = 7;

static const char ntpServerName[] = "ntp1.aliyun.com"; //NTP服务器，阿里云
const int timeZone = 8;                                //时区，北京时间为+8
WiFiUDP Udp;   //UDP服务器
unsigned int localPort = 8888; // 用于侦听UDP数据包的本地端口
time_t getNtpTime();           //获取NPT时间函数
boolean isNTPConnected = false;//初始化NPT连接失败

String response_ws;            //json返回天气信息
String GetUrl;                 //心知天气url请求
uint8_t spi_data[16];


//------------------------------------------------------------------
//-------------------------中断执行函数-------------------------------
//------------------------------------------------------------------
ICACHE_RAM_ATTR void onChange()
{
    delay(100);  //延时消抖
    if(digitalRead(15) == HIGH)
    {
        Click ++; //按键按下变量自增
        Serial.println("Key Down");
    }
    
}

//---------------------------setup函数-------------------------------

void setup()
{
    spi_data[0]=0x55;
    spi_data[15]=0xed;
  
    Serial.begin(9600);  //串口通讯波特率
   // ESP_Serial.begin(9600);
  //  while (!Serial)
  //      continue;
    SPI.setDataMode(SPI_MODE0);
    SPI.setBitOrder(MSBFIRST);//传输顺序高位在前
    SPI.setClockDivider(SPI_CLOCK_DIV2);
    //SPI.setFrequency(80000000);
    SPI.pins(14, 12, 13, 15);
    SPI.begin();
    Serial.print("Connecting WiFi...");
    WiFi.mode(WIFI_STA);
    WiFi.begin(ssid, password);  //连接WIFI
    while (WiFi.status() != WL_CONNECTED){
        delay(500);
        Serial.print(".");
    }
    Serial.println("");
    Serial.println("WiFi connected");
    Serial.println("IP address: ");
    Serial.println(WiFi.localIP());

    Serial.println("Starting UDP");
    Udp.begin(localPort);//UDP初始化
    Serial.print("Local port: ");
    Serial.println(Udp.localPort());
    Serial.println("waiting for sync");
    setSyncProvider(getNtpTime);//获取NPT时间
    setSyncInterval(300); //每300秒同步一次时间
    isNTPConnected = true;//获取成功标志

}


//-----------------------NTP_clock----------------------------------

const int NTP_PACKET_SIZE = 48;     // NTP时间在消息的前48个字节里
byte packetBuffer[NTP_PACKET_SIZE]; // NPT时间输入输出包的缓冲区

//NPT时间获取函数
time_t getNtpTime()
{
  IPAddress ntpServerIP;          // NTP服务器的地址

  while (Udp.parsePacket() > 0);  // 丢弃以前接收的任何数据包
  Serial.println("Transmit NTP Request");
  // 从池中获取随机服务器
  //ntpServerName=ntp1.aliyun.com
  WiFi.hostByName(ntpServerName, ntpServerIP);
  Serial.print(ntpServerName);
  Serial.print(": ");
  Serial.println(ntpServerIP);
  sendNTPpacket(ntpServerIP);   //发送NPT IP地址
  uint32_t beginWait = millis();
  while (millis() - beginWait < 1500)
  {
    int size = Udp.parsePacket();
    if (size >= NTP_PACKET_SIZE)//NTP_PACKET_SIZE=48
    {
      Serial.println("Receive NTP Response");
      isNTPConnected = true;    //NPT连接标志置1
      Udp.read(packetBuffer, NTP_PACKET_SIZE); // 将数据包读取到NPT时间信息缓冲区
      unsigned long secsSince1900;
      // 将从位置40开始的四个字节转换为长整型，只取前32位整数部分
      secsSince1900 = (unsigned long)packetBuffer[40] << 24;
      secsSince1900 |= (unsigned long)packetBuffer[41] << 16;
      secsSince1900 |= (unsigned long)packetBuffer[42] << 8;
      secsSince1900 |= (unsigned long)packetBuffer[43];
      Serial.println(secsSince1900);
      Serial.println(secsSince1900 - 2208988800UL + timeZone * SECS_PER_HOUR);
      return secsSince1900 - 2208988800UL + timeZone * SECS_PER_HOUR;
    }
  }
  //如果没反应....
  Serial.println("No NTP Response :-("); //无NTP响应
  isNTPConnected = false;
  return 0; //如果未得到时间则返回0
}


// 向给定地址的时间服务器发送NTP请求
void sendNTPpacket(IPAddress& address)
{
  memset(packetBuffer, 0, NTP_PACKET_SIZE);
  packetBuffer[0] = 0b11100011; // LI, Version, Mode
  packetBuffer[1] = 0;          // Stratum, or type of clock
  packetBuffer[2] = 6;          // Polling Interval
  packetBuffer[3] = 0xEC;       // Peer Clock Precision
  // 8 bytes of zero for Root Delay & Root Dispersion
  packetBuffer[12] = 49;
  packetBuffer[13] = 0x4E;
  packetBuffer[14] = 49;
  packetBuffer[15] = 52;
  Udp.beginPacket(address, 123); //NTP需要使用的UDP端口号为123
  Udp.write(packetBuffer, NTP_PACKET_SIZE);
  Udp.endPacket();
}

//NPT时、分、秒转换
void NTP_TIME() 
{
    uint8_t year_H,year_L, months,days,hours, minutes, seconds,weekdays;
    uint16_t years;
    years= year();
    year_H=(years>>8)&0xff;
    year_L=years&0xff;
    months =month();
    days =day();
    weekdays =weekday();
    hours = hour();
    minutes = minute();
    seconds = second();

    spi_data[7]=year_H;//
    spi_data[8]=year_L;
    spi_data[9]=months;//
    spi_data[10]=days;
    spi_data[11]=weekdays;//
    spi_data[12]=hours;
    spi_data[13]=minutes;//
    spi_data[14]=seconds;
    
    //Serial.printf("%d%d-%d-%d--%d%d:%d:%d\n",year_H,year_L,months,days,weekdays,hours, minutes, seconds);
   
}


//https://api.seniverse.com/v3/weather/now.json?key=SuQDmnIsehmcwH7cx&location=beijing&language=zh-Hans&unit=c
//https://api.seniverse.com/v3/weather/daily.json?key=SuQDmnIsehmcwH7cx&location=beijing&language=zh-Hans&unit=c&start=0&days=5

//构建并发出获取天气数据的请求，获得json数据
bool getJson_ws()  
{
    bool s = false;//json数据获取成功标志
    GetUrl = String(HOST) + "/v3/weather/daily.json?key=";  //HOST = "http://api.seniverse.com";
    GetUrl += APIKEY;
    GetUrl += "&location=";
    GetUrl += CITY;
    GetUrl += "&language=";
    GetUrl += LANGUAGE;
    GetUrl += "&unit=c&start=0&days=3";
    http.setTimeout(HTTP_TIMEOUT);
    http.begin(GetUrl);
    int httpCode = http.GET();
    if (httpCode > 0)
    {
        if (httpCode == HTTP_CODE_OK)
        {
            response_ws = http.getString();
            //Serial.println(response_ws);
            s = true;//json天气数据获取成功
        }
    }
    else
    {
        Serial.printf("[HTTP] GET JSON failed, error: %s\n", http.errorToString(httpCode).c_str());
        s = false;
    }
    http.end();
    return s;
}

//对心知天气的json数据解析，采用arduinojson v6库
bool parseJson_ws(String json)  
{
    const size_t capacity = JSON_ARRAY_SIZE(1) + JSON_ARRAY_SIZE(3) + JSON_OBJECT_SIZE(1) + JSON_OBJECT_SIZE(3) + JSON_OBJECT_SIZE(6) + 3*JSON_OBJECT_SIZE(14) + 810;
    DynamicJsonDocument doc(capacity);  //根据json数据结构计算所需要的内存大小
    deserializeJson(doc, json);  //反序列化json数据
    JsonObject results_0 = doc["results"][0];
    JsonArray results_0_daily = results_0["daily"];

    JsonObject results_0_daily_0 = results_0_daily[0];  //今天的天气数据
    const char* results_0_daily_0_date = results_0_daily_0["date"]; // "2020-06-20"
    const char* results_0_daily_0_text_day = results_0_daily_0["text_day"]; // "多云"   文字形式
    const char* results_0_daily_0_code_day = results_0_daily_0["code_day"]; // "4"     代码形式
    const char* results_0_daily_0_text_night = results_0_daily_0["text_night"]; // "多云"
    const char* results_0_daily_0_code_night = results_0_daily_0["code_night"]; // "4"
    const char* results_0_daily_0_high = results_0_daily_0["high"]; // "28"  最高气温
    const char* results_0_daily_0_low = results_0_daily_0["low"]; // "20"    最低气温
    const char* results_0_daily_0_rainfall = results_0_daily_0["rainfall"]; // "0.0"
    const char* results_0_daily_0_precip = results_0_daily_0["precip"]; // ""
    const char* results_0_daily_0_wind_direction = results_0_daily_0["wind_direction"]; // "东"  风向
    const char* results_0_daily_0_wind_direction_degree = results_0_daily_0["wind_direction_degree"]; // "90"  风向度数
    const char* results_0_daily_0_wind_speed = results_0_daily_0["wind_speed"]; // "16.20"  风速
    const char* results_0_daily_0_wind_scale = results_0_daily_0["wind_scale"]; // "3"      风力级别
    const char* results_0_daily_0_humidity = results_0_daily_0["humidity"]; // "72"  湿度

    JsonObject results_0_daily_1 = results_0_daily[1];  //明天的天气数据
    const char* results_0_daily_1_date = results_0_daily_1["date"]; // "2020-06-21"
    const char* results_0_daily_1_text_day = results_0_daily_1["text_day"]; // "多云"
    const char* results_0_daily_1_code_day = results_0_daily_1["code_day"]; // "4"
    const char* results_0_daily_1_text_night = results_0_daily_1["text_night"]; // "阴"
    const char* results_0_daily_1_code_night = results_0_daily_1["code_night"]; // "9"
    const char* results_0_daily_1_high = results_0_daily_1["high"]; // "27"
    const char* results_0_daily_1_low = results_0_daily_1["low"]; // "20"
    const char* results_0_daily_1_rainfall = results_0_daily_1["rainfall"]; // "0.0"
    const char* results_0_daily_1_precip = results_0_daily_1["precip"]; // ""
    const char* results_0_daily_1_wind_direction = results_0_daily_1["wind_direction"]; // "东南"
    const char* results_0_daily_1_wind_direction_degree = results_0_daily_1["wind_direction_degree"]; // "135"
    const char* results_0_daily_1_wind_speed = results_0_daily_1["wind_speed"]; // "16.20"
    const char* results_0_daily_1_wind_scale = results_0_daily_1["wind_scale"]; // "3"
    const char* results_0_daily_1_humidity = results_0_daily_1["humidity"]; // "68"

    JsonObject results_0_daily_2 = results_0_daily[2];  //后天的天气数据
    const char* results_0_daily_2_date = results_0_daily_2["date"]; // "2020-06-22"
    const char* results_0_daily_2_text_day = results_0_daily_2["text_day"]; // "中雨"
    const char* results_0_daily_2_code_day = results_0_daily_2["code_day"]; // "14"
    const char* results_0_daily_2_text_night = results_0_daily_2["text_night"]; // "小雨"
    const char* results_0_daily_2_code_night = results_0_daily_2["code_night"]; // "13"
    const char* results_0_daily_2_high = results_0_daily_2["high"]; // "26"
    const char* results_0_daily_2_low = results_0_daily_2["low"]; // "23"
    const char* results_0_daily_2_rainfall = results_0_daily_2["rainfall"]; // "10.0"
    const char* results_0_daily_2_precip = results_0_daily_2["precip"]; // ""
    const char* results_0_daily_2_wind_direction = results_0_daily_2["wind_direction"]; // "东南"
    const char* results_0_daily_2_wind_direction_degree = results_0_daily_2["wind_direction_degree"]; // "127"
    const char* results_0_daily_2_wind_speed = results_0_daily_2["wind_speed"]; // "25.20"
    const char* results_0_daily_2_wind_scale = results_0_daily_2["wind_scale"]; // "4"
    const char* results_0_daily_2_humidity = results_0_daily_2["humidity"]; // "71"
    
    const char* results_0_last_update = results_0["last_update"];  //数据更新时间
    
    String date0 = results_0_daily_0_date;  //将日期取出处理
    String date1 = results_0_daily_1_date;  
    String date2 = results_0_daily_2_date; 
    
    day0.date_m = date0.substring(5, 7);  //日期字符串切片
    day0.date_d = date0.substring(8, 10);
    day0.code_day = atoi(results_0_daily_0_code_day);//获取今天天气信息
    day0.high = atoi(results_0_daily_0_high);  //最高温度
    day0.low = atoi(results_0_daily_0_low);  //最低温度
    day0.wind_direction_degree = atoi(results_0_daily_0_wind_direction_degree);  //风向
    day0.wind_scale = atoi(results_0_daily_0_wind_scale);  //风力等级
    day0.humidity = atoi(results_0_daily_0_humidity);  //湿度
    
    day1.date_m = date1.substring(5, 7);
    day1.date_d = date1.substring(8, 10);
    day1.code_day = atoi(results_0_daily_1_code_day);//获取明天天气信息
    day1.high = atoi(results_0_daily_1_high);
    day1.low = atoi(results_0_daily_1_low);
    day1.wind_direction_degree = atoi(results_0_daily_1_wind_direction_degree);
    day1.wind_scale = atoi(results_0_daily_1_wind_scale);
    day1.humidity = atoi(results_0_daily_1_humidity);

    day2.date_m = date2.substring(5, 7);
    day2.date_d = date2.substring(8, 10);
    day2.code_day = atoi(results_0_daily_2_code_day);//获取后天天气信息
    day2.high = atoi(results_0_daily_2_high);
    day2.low = atoi(results_0_daily_2_low);
    day2.wind_direction_degree = atoi(results_0_daily_2_wind_direction_degree);
    day2.wind_scale = atoi(results_0_daily_2_wind_scale);
    day2.humidity = atoi(results_0_daily_2_humidity);

    return true;
}

 //串口打印显示今天的天气信息
void display_today() 
{   
   spi_data[1]=day0.code_day;//天气代码
   spi_data[2]=day0.high;//
   spi_data[3]=day0.low;//
   spi_data[4]=day0.wind_direction_degree;//
   spi_data[5]=day0.wind_scale;//
   spi_data[6]=day0.humidity;
   //Serial.printf("天气:%d 最高:%d 最低:%d 风向:%d 风力:%d 湿度:%d\n",spi_data[1],spi_data[2],spi_data[3],spi_data[4],spi_data[5],spi_data[6]);
}

//获取解析json，显示今天天气
void weather_station0()  
{   
    if (getJson_ws())//如果json天气数据获取成功
    {
        if (parseJson_ws(response_ws))//对心知天气的json数据解析成功，response_ws全局string变量
        {
          display_today();//打印显示今天的信息
        }
    }
}

//---------------------------loop函数--------------------------------
void loop()  //主循环，判断按键次数切换运行程序和显示
{
  Serial.write(spi_data[0]);
    if (WiFi.status() == WL_CONNECTED)
    {  
      //NPT年、月、日、星期、时、分、秒转换
      NTP_TIME();
      weather_station0(); 
      for(int8_t i=0; i<16; i++)  /* transfer buff data per second */
      {
      //ESP_Serial.write(spi_data[i]);
      Serial.write(spi_data[i]);
      }
      //SPI.transfer(spi_data[i]);
      delay(20);
    }
    else
    {
        Serial.println("[WiFi] Waiting to reconnect...");  //未联网串口输出
    }
}
